using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;

namespace Slobby.Client.Net {
    public class Connection {
        string address; int port;

        TcpClient connection;
        Thread listenerThread, sendThread;

        Stream serializationStream;

        Queue<object> sendQueue;

        volatile bool disconectQueued = false;

        public event EventHandler Connected;
        public event Action<string> Disconnected;
        public event Action<string> FailedConnect;
        public event Action<object> MessageRecieved;

        public Connection(string address, int port) {
            listenerThread = new Thread(SetUp);
            listenerThread.IsBackground = true;
            sendThread = new Thread(SendLoop);
            sendThread.IsBackground = true;
            sendQueue = new Queue<object>();
            connection = new TcpClient();
            this.address = address;
            this.port = port;
        }

        public void Connect() {
            listenerThread.Start();
        }

        public void Disconnect() {
            if (!connection.Connected)
                return;
            disconectQueued = true;
        }

        public void SendMessage(object o) {
            lock (sendQueue) {
                sendQueue.Enqueue(o);
            }
        }

        private void RaiseDisconnect(string message) {
            if (Disconnected != null)
                Disconnected(message);

            System.Diagnostics.Debug.WriteLine("discon:" + message);
        }

        private void SetUp() {
            try {
                connection.Connect(address, port);
            }
            catch (Exception ex) {
                if (FailedConnect != null)
                    FailedConnect("Could not connect to " + address.ToString() + "; " + ex.Message);
                return;
            }

            if (Connected != null)
                Connected(this, new EventArgs());

            serializationStream = connection.GetStream();
            sendThread.Start();
            RecieveLoop();
        }

        private void RecieveLoop() {
            try {
                while (serializationStream.CanRead) {
                    object o = Program.NetworkFormatter.Deserialize(serializationStream);
                    MessageRecieved(o);
                }
            }
            catch (Exception ex) {
                //RaiseDisconnect("Server connection terminated [" + ex.Message + " in RecieveLoop]");
            }
        }

        private void SendLoop() {
            try {
                while (connection.Connected) {
                    lock (sendQueue) {
                        while (sendQueue.Count > 0)
                            Program.NetworkFormatter.Serialize(serializationStream, sendQueue.Dequeue());
                    }
                    if (disconectQueued)
                        break;
                    Thread.Sleep(100);
                }
            }
            catch (Exception ex) {
                RaiseDisconnect("Server connection terminated [" + ex.Message + " in SendLoop]");
            }
            if (disconectQueued)
                RaiseDisconnect("Disconnect Requested");
            else
                RaiseDisconnect("Connection terminated [SendLoop()]");
            connection.Close();
        }
    }
}